/**
 * WebRTC相关
 *
 * @Date 2021/4/26 09:56
 * @author 周世焕
 */

import {sendMessage} from "@/api/im";
import {encodeRtcConnectMessage} from "@/modules/im/codec/encoder";
import {addListener} from "@/modules/im/command/base";
import {rtcListeners} from "@/modules/im/command/listeners";
import Constants from '@/modules/im/constants'

addListener(rtcListeners,'RTC',function (content) {
    switch (content[Constants.Rtc.PROPERTY_CMD_TYPE]) {
        case 'answer':
            console.log('got an answer')
            resolveAnswer(content[Constants.Rtc.PROPERTY_PAYLOAD])
            break
        case 'candidate':
            console.log('got an answer')
            resolveCandidate(content[Constants.Rtc.PROPERTY_PAYLOAD])
            break
        case 'offer':
            console.log('got an offer')
            acceptCall(content[Constants.Rtc.PROPERTY_LOCAL],content[Constants.Rtc.PROPERTY_PAYLOAD])
            break
        default:
            break
    }
})

let startTime;
let localVideo;
let remoteVideo;
let localStream;
const configuration = {
    "iceServers":[
        {"urls":"stun:49.235.121.233:3478"},
        {"urls":"turn:49.235.121.233:3478","username":"kurento","credential":"kurento" }
    ]
};
let connection = new RTCPeerConnection(configuration);
let remoteUserId;

const offerOptions = {
    offerToReceiveAudio: 1,
    offerToReceiveVideo: 1
};

let listener = {}
export function setListener(l) {
    listener = l
}

/**
 * 开启视频流
 * @param localVideo
 * @param remoteVideo
 * @returns {Promise<boolean>} 是否成功开启
 */
export async function start(local, remote) {
    if(connection == null) {
        connection = new RTCPeerConnection(configuration);
    }
    console.log('请求本地媒体流');
    localVideo = local
    localVideo.addEventListener('loadedmetadata', function() {
        console.log(`本地视频参数，宽度: ${this.videoWidth}px,  高度: ${this.videoHeight}px`);
    });
    remoteVideo = remote
    remoteVideo.addEventListener('loadedmetadata', function() {
        console.log(`远程视频参数，宽度: ${this.videoWidth}px,  高度: ${this.videoHeight}px`);
    });
    try {
        const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
        console.log('成功获取本地媒体流');
        localVideo.srcObject = stream;
        localStream = stream;
        return true
    } catch (e) {
        console.log(`getUserMedia() error: ${e.name}`);
        return false
    }
}

/**
 * 初始化对等连接
 * @returns {Promise<void>}
 */
async function initPeerConnection() {
    startTime = window.performance.now();
    const videoTracks = localStream.getVideoTracks();
    const audioTracks = localStream.getAudioTracks();
    if (videoTracks.length > 0) {
        console.log(`使用视频设备: ${videoTracks[0].label}`);
    }
    if (audioTracks.length > 0) {
        console.log(`使用音频设备: ${audioTracks[0].label}`);
    }
    connection.addEventListener('icecandidate', e => {
        sendMessage(encodeRtcConnectMessage(remoteUserId,'candidate',e.candidate))
    });
    connection.addEventListener('iceconnectionstatechange', e => onIceStateChange(connection, e));
    connection.addEventListener('track', gotRemoteStream);
    localStream.getTracks().forEach(track => connection.addTrack(track, localStream));
    console.log('添加本地流到PeerConnection');
}

/**
 * 创建呼叫请求Offer，发送给对方
 * @param toId
 * @returns {Promise<void>}
 */
export async function startCall(toId) {
    remoteUserId = toId
    console.log('开始呼叫对方');
    await initPeerConnection();
    try {
        console.log('开始创建呼叫Offer');
        const offer = await connection.createOffer(offerOptions);
        await connection.setLocalDescription(offer);
        //将Offer发送给对方
        sendMessage(encodeRtcConnectMessage(remoteUserId,'offer',offer))
    } catch (e) {
        onCreateSessionDescriptionError(e);
    }
}

/**
 * 接受呼叫请求，将呼叫Offer保存，并创建Answer返回给对方
 * @param toId
 * @param offer
 * @returns {Promise<void>}
 */
export async function acceptCall(toId,offer) {
    remoteUserId = toId
    console.log('接受对方呼叫');
    await initPeerConnection();
    try {
        console.log('设置通话对方传过来的Offer');
        try {
            await connection.setRemoteDescription(offer);
        } catch (e) {
            onSetSessionDescriptionError();
        }
        console.log('创建应答Answer给对方');
        try {
            const answer = await connection.createAnswer();
            try {
                await connection.setLocalDescription(answer);
                //向对方发送Answer
                sendMessage(encodeRtcConnectMessage(remoteUserId,'answer',answer))
            } catch (e) {
                onSetSessionDescriptionError(e);
            }
        } catch (e) {
            onCreateSessionDescriptionError(e);
        }
    } catch (e) {
        onCreateSessionDescriptionError(e);
    }
}

/**
 * 处理Answer应答，准备建立连接，后续还需要进行ICE候选
 * @param answer
 * @returns {Promise<void>}
 */
export async function resolveAnswer(answer) {
    try {
        await connection.setRemoteDescription(answer);
    } catch (e) {
        onSetSessionDescriptionError();
    }
}

export async function resolveCandidate(candidate) {
    try {
        await connection.addIceCandidate(candidate);
    } catch (e) {
        onSetSessionDescriptionError();
    }
}

function onCreateSessionDescriptionError(error) {
    console.log(`Failed to create session description: ${error.toString()}`);
}

function onSetSessionDescriptionError(error) {
    console.log(`Failed to set session description: ${error.toString()}`);
}

function gotRemoteStream(e) {
    if (remoteVideo.srcObject !== e.streams[0]) {
        remoteVideo.srcObject = e.streams[0];
    }
}

function onIceStateChange(pc, event) {
    if (pc) {
        console.log(`ICE state: ${pc.iceConnectionState}`);
        if(listener.hasOwnProperty('oniceconnectionstatechange')) {
            listener.oniceconnectionstatechange(pc.iceConnectionState)
        }
    }
}

export function hangup() {
    localStream.getTracks().forEach(track => {
        track.stop()
    })
    connection.close();
    connection = null
}
